package org.apache.osgimaker.editors.content;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.osgimaker.editors.model.Bundle;
import org.apache.osgimaker.editors.model.BundleListEditModel;
import org.apache.osgimaker.utils.BundleListTableLabelProvider;
import org.apache.osgimaker.utils.PackageDropAdapter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.ui.plugin.AbstractUIPlugin;

public class BundleListPart extends SectionPart implements
		PropertyChangeListener {

	private BundleListEditModel model;
	private List<String> bundles = new ArrayList<String>();

	private Table table;
	private TableViewer viewer;
	private IManagedForm managedForm;

	public BundleListPart(Composite parent, FormToolkit toolkit, int style) {
		super(parent, toolkit, style);
		createSection(getSection(), toolkit);
	}

	void createSection(Section section, FormToolkit toolkit) {
		section.setText("Bundle List");
		section.setDescription("This project can be exported as the following bundles.");

		ToolBar toolbar = new ToolBar(section, SWT.FLAT);
		section.setTextClient(toolbar);
		// private final Image imgInsert =
		// AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID,
		// "/icons/table_row_insert.png").createImage();

		final ToolItem exportItem = new ToolItem(toolbar, SWT.PUSH);
		exportItem.setImage(AbstractUIPlugin.imageDescriptorFromPlugin(
				"org.apache.osgimaker.ui", "/icons/export_bundles.png")
				.createImage());
		exportItem.setToolTipText("Export Bundles");

		final ToolItem addItem = new ToolItem(toolbar, SWT.PUSH);
		addItem.setImage(PlatformUI.getWorkbench().getSharedImages()
				.getImage(ISharedImages.IMG_OBJ_ADD));
		addItem.setToolTipText("Add");

		final ToolItem removeItem = new ToolItem(toolbar, SWT.PUSH);
		removeItem.setImage(PlatformUI.getWorkbench().getSharedImages()
				.getImage(ISharedImages.IMG_TOOL_DELETE));
		removeItem.setDisabledImage(PlatformUI.getWorkbench().getSharedImages()
				.getImage(ISharedImages.IMG_TOOL_DELETE_DISABLED));
		removeItem.setToolTipText("Remove");
		removeItem.setEnabled(false);

		Composite composite = toolkit.createComposite(section);
		section.setClient(composite);

		table = toolkit.createTable(composite, SWT.FULL_SELECTION | SWT.MULTI
				| SWT.BORDER);

		viewer = new TableViewer(table);
		viewer.setContentProvider(new ArrayContentProvider());
		viewer.setLabelProvider(new BundleListTableLabelProvider());

		// Listeners
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				managedForm.fireSelectionChanged(BundleListPart.this,
						event.getSelection());
				removeItem.setEnabled(!viewer.getSelection().isEmpty());
			}
		});
		viewer.addDropSupport(
				DND.DROP_COPY | DND.DROP_MOVE,
				new Transfer[] { LocalSelectionTransfer.getTransfer(),
						TextTransfer.getInstance(),
						ResourceTransfer.getInstance() },
				new PackageDropAdapter<String>(viewer) {
					@Override
					protected String createNewEntry(String packageName) {
						return packageName;
					}

					@Override
					protected void addRows(int index, Collection<String> rows) {
						if (index == -1) {
							bundles.addAll(rows);
							viewer.add(rows.toArray());
						} else {
							bundles.addAll(index, rows);
							viewer.refresh();
						}
						viewer.setSelection(new StructuredSelection(rows));
						markDirty();
					}

					@Override
					protected int indexOf(Object object) {
						return 0;
					}
				});
		table.addKeyListener(new KeyAdapter() {
			@Override
			public void keyReleased(KeyEvent e) {
				if (e.character == SWT.DEL) {
					// doRemovePackages();
				} else if (e.character == '+') {
					;
					// doAddPackages();
				}
			}
		});
		addItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				doAddBundles();
			}
		});
		removeItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				doRemovePackages();
			}
		});
		exportItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				doExportBundles();
			}
		});

		GridLayout layout = new GridLayout(1, false);
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		composite.setLayout(layout);

		GridData gd;

		gd = new GridData(SWT.FILL, SWT.FILL, true, true);
		gd.heightHint = 75;
		gd.widthHint = 75;
		table.setLayoutData(gd);
	}

	private void doAddBundles() {
		InputDialog input = new InputDialog(getSection().getShell(),
				"Add new OSGi bundle",
				"Please input OSGi bundle symbolic name:", "", null);
		if (input.open() == Window.OK) {
			String newBundleName = input.getValue();
			if (newBundleName != null && !newBundleName.trim().equals("")
					&& !bundles.contains(newBundleName)) {
				List<String> added = new LinkedList<String>();
				if (bundles.add(newBundleName)) {
					added.add(newBundleName);
				}

				// Update the model and view
				if (!added.isEmpty()) {
					viewer.add(added.toArray(new String[added.size()]));
					markDirty();
				}
			}
		}
	}

	private void doRemovePackages() {
		boolean b = MessageDialog.openQuestion(getSection().getShell(),
				"Delete Confirm",
				"Are you confirm to delete this bundle content?");
		if (b) {
			IStructuredSelection selection = (IStructuredSelection) viewer
					.getSelection();
			if (!selection.isEmpty()) {
				Iterator<?> elements = selection.iterator();
				List<Object> removed = new LinkedList<Object>();
				while (elements.hasNext()) {
					Object pkg = elements.next();
					if (bundles.remove(pkg))
						removed.add(pkg);
				}

				if (!removed.isEmpty()) {
					viewer.remove(removed.toArray(new String[removed.size()]));
					markDirty();
				}
			}
		}
	}

	private void doExportBundles() {
		boolean b = MessageDialog.openQuestion(getSection().getShell(),
				"Export Confirm",
				"Are you confirm to export all these bundles?");
		if (b) {
			IEditorPart editorPart = Workbench.getInstance()
					.getActiveWorkbenchWindow().getActivePage()
					.getActiveEditor();

			if (editorPart != null) {
				IFileEditorInput input = (IFileEditorInput) editorPart
						.getEditorInput();
				IFile file = input.getFile();
				final IProject activeProject = file.getProject();

				getSection().getShell().getDisplay().asyncExec(new Runnable() {
					public void run() {
						for (Bundle bundle : model.getDataModelBundleList()) {
							String id = bundle.getId();
							IFile file = activeProject.getFile("/" + id
									+ ".bnd");
							InputStream source = new ByteArrayInputStream(
									getBundleBNDDescriptionContent(bundle)
											.getBytes());

							try {
								if (!file.exists())
									file.create(source, false, null);
								else
									file.setContents(source, false, false, null);
							} catch (CoreException e) {
								e.printStackTrace();
							}
						}

					}
				});
			}
		}
	}

	private String getBundleBNDDescriptionContent(Bundle bundle) {
		String name = "Bundle-SymbolicName: $name$\n\r";
		String version = "Bundle-Version: $version$\n\r";
		String ret = "Private-Package:";

		if (bundle.getVersion() != null
				&& !bundle.getVersion().trim().equals(""))
			ret = version.replace("$version$", bundle.getVersion()) + ret;

		if (bundle.getSymbolicName() != null
				&& !bundle.getSymbolicName().trim().equals(""))
			ret = name.replace("$name$", bundle.getSymbolicName()) + ret;

		if (bundle != null && bundle.getClasses() != null) {
			for (int i = 0; i < bundle.getClasses().size(); i++) {
				ret += bundle.getClasses().get(i).trim();
				if (i != bundle.getClasses().size() - 1)
					ret += ",\\\n\t";
			}
		}
		return ret;
	}

	@Override
	public void commit(boolean onSave) {
		super.commit(onSave);
		model.setBundleList(bundles);

		managedForm.dirtyStateChanged();
	}

	@Override
	public void refresh() {
		List<String> tmp = model.getBundleList();
		if (tmp != null)
			bundles = new ArrayList<String>(tmp);
		else
			bundles = new ArrayList<String>();
		viewer.setInput(bundles);
		super.refresh();
	}

	@Override
	public void initialize(IManagedForm form) {
		super.initialize(form);
		this.managedForm = form;
		model = (BundleListEditModel) form.getInput();
		model.addPropertyChangeListener("bundleList", this);
	}

	@Override
	public void dispose() {
		super.dispose();
		if (this.model != null)
			this.model.removePropertyChangeListener(this);
	}

	public void propertyChange(PropertyChangeEvent evt) {
		IFormPage page = (IFormPage) getManagedForm().getContainer();
		if (page.isActive()) {
			refresh();
		} else {
			markStale();
		}
	}

	private IJavaProject getJavaProject() {
		IFormPage page = (IFormPage) getManagedForm().getContainer();
		IEditorInput input = page.getEditorInput();
		if (!IFileEditorInput.class.isInstance(input)) {
			return null;
		}
		IProject project = ((IFileEditorInput) input).getFile().getProject();
		return JavaCore.create(project);
	}

	public ISelectionProvider getSelectionProvider() {
		return viewer;
	}
}
